package cn.touna.jss.modules.adapter;

import cn.touna.jss.common.enums.IntfScopeEnum;
import cn.touna.risk.api.exception.BusinessException;
import cn.touna.risk.api.exception.ServiceException;
import org.springframework.aop.support.AopUtils;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Component;
import org.springframework.util.Assert;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;

/**
 * api初始化工厂
 * Created by wuwc on 2017/11/30.
 */
@Component
public class ApiFactory{

    private ApplicationContext applicationContext;

    private Map<String, ApiRunnable> apiMap = new HashMap<>();

    public ApiFactory(ApplicationContext applicationContext) {
        Assert.notNull(applicationContext);
        this.applicationContext = applicationContext;
    }

    public void loadApiBeans() throws InstantiationException, IllegalAccessException {
        String[] names = applicationContext.getBeanDefinitionNames();
        Class<?> type;
        for (String name : names) {
            if (AopUtils.isCglibProxy(applicationContext.getBean(name))) {
                type = AopUtils.getTargetClass(applicationContext.getBean(name));
            } else {
                type = applicationContext.getType(name);
            }
            for (Method method : type.getDeclaredMethods()) {
                ApiMapping apiMapping = method.getAnnotation(ApiMapping.class);
                if (apiMapping != null) {
                    addApiItem(apiMapping, name, method);
                }
            }
        }
    }

    public Map<String, ApiRunnable> getApiMap() {
        return apiMap;
    }

    public ApiRunnable getApiRunnable(String apiName) {
        return apiMap.get(apiName);
    }

    private void addApiItem(ApiMapping apiMapping, String beanName, Method method) {
        ApiRunnable apiRunnable = new ApiRunnable();
        apiRunnable.apiName = apiMapping.value();
        apiRunnable.apiDesc = apiMapping.desc();
        apiRunnable.targetName = beanName;
        apiRunnable.targetMethod = method;
        apiRunnable.scope=apiMapping.scope();
        apiRunnable.version=apiMapping.version();
        if (apiMap.containsKey(apiMapping.value())) {
            throw new ServiceException("存在重复的接口:" + apiMapping.value());
        }
        apiMap.put(apiMapping.value(), apiRunnable);
    }

    public class ApiRunnable {

        String apiName;

        String apiDesc;

        String targetName;

        Object target;

        Method targetMethod;

        String version;

        IntfScopeEnum scope;

        public Object run(Object... args) throws InvocationTargetException, IllegalAccessException, BusinessException {

            if (target == null) {
                target = applicationContext.getBean(targetName);
            }
            return targetMethod.invoke(target, args);
        }

        public Class<?>[] getParamTypes() {

            return targetMethod.getParameterTypes();
        }

        public String getApiName() {
            return apiName;
        }

        public String getTargetName() {
            return targetName;
        }

        public Object getTarget() {
            return target;
        }

        public Method getTargetMethod() {
            return targetMethod;
        }

        public String getApiDesc() {
            return apiDesc;
        }

        public void setApiDesc(String apiDesc) {
            this.apiDesc = apiDesc;
        }

        public String getVersion() {
            return version;
        }

        public void setVersion(String version) {
            this.version = version;
        }

        public IntfScopeEnum getScope() {
            return scope;
        }

        public void setScope(IntfScopeEnum scope) {
            this.scope = scope;
        }
    }
}
